package com.tbit.uqbike.client.remote;

import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.squareup.okhttp.HttpUrl;
import com.tbit.uqbike.client.constant.WeixinConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Scanner;

/**
 * 支付分签名加密
 *
 * @author lmy
 * @date 2020/08/12 1:00
 */
@Slf4j
public class PayScore {


    /**
     * 微信支付API v3 签名
     *
     * @param method       请求类型GET、POST
     * @param url          请求地址
     * @param body         请求数据 GET: 传"" POST: json串
     * @param merchantId   商户号
     * @param certSerialNo 证书序列号
     * @param filename     API证书相对路径
     * @return Authorization 请求头信息
     * @throws Exception
     */
    public static String getToken(String method, String url, String body, String merchantId, String certSerialNo, String filename) throws Exception {
        String signStr = "";
        HttpUrl httpurl = HttpUrl.parse(url);
        // 随机字符串
        String nonceStr = RandomNumber.getRandomString(32);
        // 时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        if (StringUtils.isEmpty(body)) {
            body = "";
        }
        //构造签名串
        String message = buildMessage(method, httpurl, timestamp, nonceStr, body);
        //对签名串进行加密
        String signature = sign(message.getBytes("utf-8"), filename);//
        System.out.println("=====" + filename);
        System.out.println("------------" + signature);
        signStr = "mchid=\"" + merchantId
                + "\",nonce_str=\"" + nonceStr
                + "\",timestamp=\"" + timestamp
                + "\",serial_no=\"" + certSerialNo
                + "\",signature=\"" + signature + "\"";
        log.info("响应字符串：signStr：" + signStr);
        return signStr;
    }

    /**
     * 构造签名串
     *
     * @param method    获取HTTP请求的方法（GET,POST,PUT等）
     * @param url       获取请求的绝对URL，并去除域名部分得到参与签名的URL。如果请求中有查询参数，URL末尾应附加有'?'和对应的查询字符串。
     * @param timestamp 获取发起请求时的系统当前时间戳
     * @param nonceStr  生成一个请求随机串
     * @param body      获取请求中的请求报文主体（request body）  get请求，报文为空；post，put请求，真是发送JSON；图片上传，meta对应的json
     * @return 按照描 述规则，构造的请求签名串为：
     */
    public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * 对签名数据进行签名
     *
     * @param message  签名数据
     * @param filename 商户私钥路径
     * @return 签名值
     * @throws Exception
     */
    public static String sign(byte[] message, String filename) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(filename));
        sign.update(message);
        return Base64.encodeBase64String(sign.sign());
    }

    /**
     * 获取私钥。
     *
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {
        // 编译后的相对路径
        //ClassPathResource classPathResource = new ClassPathResource(filename);
        //InputStream inputStream = classPathResource.getInputStream();
        InputStream inputStream = new FileInputStream(new File(filename));
        Scanner scanner = new Scanner(inputStream, "UTF-8");
        String content = scanner.useDelimiter("\\A").next();
        // 绝对路径
        //String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            log.error("异常" + e);
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 获取证书。
     *
     * @param filename 证书文件路径  (required)
     * @return X509证书
     */
    public static X509Certificate getCertificate(String filename) throws IOException {
        InputStream fis = new FileInputStream(filename);
        BufferedInputStream bis = new BufferedInputStream(fis);
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        } finally {
            bis.close();
        }
    }

    public static void main(String[] args) throws IOException {
        String data;
        try {
            data = HttpRequest.post(WeixinConstant.establishOrderUrl)
                    .header(Header.CONTENT_TYPE, "application/json")
                    .header(Header.ACCEPT, "application/json")
                    // 签名
                    .header("Authorization", "WECHATPAY2-SHA256-RSA2048" + " "
                            + PayScore
                            .getToken("POST", WeixinConstant.establishOrderUrl, "", "1739937432",
                                    "5EE337E7722CDD0CEACA9DAF16E6DCB9E1862D11", "C:\\Users\\tbit\\Desktop\\1701087532_20241212_cert (4)\\apiclient_key.pem"))
                    .execute().body();
            JSONObject workOrder = JSONObject.parseObject(data);
            log.info("创建支付分订单返回值:   " + workOrder);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            log.info("创建订单异常", e);
        }
    }
}
